package com.eedi.framework.social.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.exception.ServiceException;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.social.controller.sys.vo.SysSocialUserBindReq;
import com.eedi.framework.social.controller.sys.vo.SysSocialUserResp;
import com.eedi.framework.social.controller.sys.vo.user.SysSocialUserPageReq;
import com.eedi.framework.social.dal.dataobject.SysSocialUserBindDO;
import com.eedi.framework.social.dal.dataobject.SysSocialUserDO;
import com.eedi.framework.social.dal.mapper.SysSocialUserBindMapper;
import com.eedi.framework.social.dal.mapper.SysSocialUserMapper;
import com.eedi.framework.social.enmus.SysSocialTypeEnum;
import com.xingyuv.jushauth.model.AuthUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertSet;
import static com.eedi.framework.common.util.json.JsonUtils.toJsonString;
import static com.eedi.sys.enums.SystemErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;
import static com.eedi.framework.social.enmus.SysSocialErrorCodeConstants.SYS_SOCIAL_USER_NOT_FOUND;

/**
 * 社交用户 Service 实现类
 *
 * @author 永聚长青源码
 */
@Service
@Validated
@Slf4j
public class SysSocialUserServiceImpl implements SysSocialUserService {

    @Resource
    private SysSocialUserBindMapper sysSocialUserBindMapper;
    @Resource
    private SysSocialUserMapper sysSocialUserMapper;

    @Resource
    private SysSocialClientService sysSocialClientService;

    @Override
    public List<SysSocialUserDO> getSocialUserList(String userId, UserTypeEnum userType) {
        // 获得绑定
        List<SysSocialUserBindDO> socialUserBinds = sysSocialUserBindMapper.selectListByUserIdAndUserType(userId, userType);
        if (CollUtil.isEmpty(socialUserBinds)) {
            return Collections.emptyList();
        }
        // 获得社交用户
        return sysSocialUserMapper.selectBatchIds(convertSet(socialUserBinds, SysSocialUserBindDO::getSysSocialUserId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bindSocialUser(SysSocialUserBindReq reqDTO) {
        // 获得社交用户
        SysSocialUserDO socialUser = authSocialUser(reqDTO.getSysSocialType(), reqDTO.getUserType(),
                reqDTO.getCode(), reqDTO.getState());
        Assert.notNull(socialUser, "社交用户不能为空");

        // 社交用户可能之前绑定过别的用户，需要进行解绑
        sysSocialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getSysSocialUserId());

        // 用户可能之前已经绑定过该社交类型，需要进行解绑
        sysSocialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(),
                socialUser.getSysSocialUserType());

        // 绑定当前登录的社交用户
        SysSocialUserBindDO socialUserBind = SysSocialUserBindDO.builder()
                .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
                        .sysSocialUserId(socialUser.getSysSocialUserId()).sysSocialType(socialUser.getSysSocialUserType()).build();
        sysSocialUserBindMapper.insert(socialUserBind);
        return socialUser.getOpenid();
    }

    @Override
    public void unbindSocialUser(String userId, UserTypeEnum userType, SysSocialTypeEnum socialType, String openid) {
        // 获得 openid 对应的 SocialUserDO 社交用户
        SysSocialUserDO socialUser = sysSocialUserMapper.selectByTypeAndOpenid(socialType, openid);
        if (socialUser == null) {
            throw exception(SYS_SOCIAL_USER_NOT_FOUND);
        }

        // 获得对应的社交绑定关系
        sysSocialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getSysSocialUserType());
    }

    @Override
    public SysSocialUserResp getSocialUser(UserTypeEnum userType, SysSocialTypeEnum socialType, String code, String state) {
        // 获得社交用户
        SysSocialUserDO socialUser = authSocialUser(socialType, userType, code, state);
        Assert.notNull(socialUser, "社交用户不能为空");

        // 如果未绑定的社交用户，则无法自动登录，进行报错
        SysSocialUserBindDO socialUserBind = sysSocialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
                socialUser.getSysSocialUserId());
        if (socialUserBind == null) {
            throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
        }
        return new SysSocialUserResp(socialUser.getOpenid(), socialUserBind.getUserId());
    }

    /**
     * 授权获得对应的社交用户
     * 如果授权失败，则会抛出 {@link ServiceException} 异常
     *
     * @param socialType 社交平台的类型 {@link SysSocialTypeEnum}
     * @param userType 用户类型
     * @param code     授权码
     * @param state    state
     * @return 授权用户
     */
    @NotNull
    public SysSocialUserDO authSocialUser(SysSocialTypeEnum socialType, UserTypeEnum userType, String code, String state) {
        // 优先从 DB 中获取，因为 code 有且可以使用一次。
        // 在社交登录时，当未绑定 User 时，需要绑定登录，此时需要 code 使用两次
        SysSocialUserDO socialUser = sysSocialUserMapper.selectByTypeAndCodeAnState(socialType, code, state);
        if (socialUser != null) {
            return socialUser;
        }

        // 请求获取
        AuthUser authUser = sysSocialClientService.getAuthUser(socialType, userType, code, state);
        Assert.notNull(authUser, "三方用户不能为空");

        // 保存到 DB 中
        socialUser = sysSocialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid());
        if (socialUser == null) {
            socialUser = new SysSocialUserDO();
        }
        // 需要保存 code + state 字段，保证后续可查询
        socialUser.setSysSocialUserType(socialType).setCode(code).setState(state)
                .setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
                .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
        if (StrUtil.isEmpty(socialUser.getSysSocialUserId()) ) {
            sysSocialUserMapper.insert(socialUser);
        } else {
            sysSocialUserMapper.updateById(socialUser);
        }
        return socialUser;
    }

    // ==================== 社交用户 CRUD ====================

    @Override
    public SysSocialUserDO getSocialUser(String id) {
        return sysSocialUserMapper.selectById(id);
    }

    @Override
    public PageResult<SysSocialUserDO> getSocialUserPage(SysSocialUserPageReq pageReqVO) {
        return sysSocialUserMapper.selectPage(pageReqVO);
    }

}
